Page Caching
============

Page caching refers to caching the content of a whole page. Page caching
can occur at different places. For example, by choosing an appropriate page
header, the client browser may cache the page being viewed for a limited
time. The Web application itself can also store the page content in cache.

Output Caching
--------------
Page caching can be considered as a special case of [fragment
caching](/doc/guide/caching.fragment). Because the content of a page is
often generated by applying a layout to a view, it will not work if we
simply call [beginCache()|CBaseController::beginCache] and
[endCache()|CBaseController::endCache] in the layout. The reason is because
the layout is applied within the [CController::render()] method AFTER the
content view is evaluated.

To cache a whole page, we should skip the execution of the action
generating the page content. We can use [COutputCache] as an action
[filter](/doc/guide/basics.controller#filter) to accomplish this task. The
following code shows how we configure the cache filter:

~~~
[php]
public function filters()
{
	return array(
		array(
			'COutputCache',
			'duration'=>100,
			'varyByParam'=>array('id'),
		),
	);
}
~~~

The above filter configuration would make the filter to be applied to all
actions in the controller. We may limit it to one or a few actions only by
using the plus operator. More details can be found in
[filter](/doc/guide/basics.controller#filter).

> Tip: We can use [COutputCache] as a filter because it extends from
[CFilterWidget], which means it is both a widget and a filter. In fact, the
way a widget works is very similar to a filter: a widget (filter) begins
before any enclosed content (action) is evaluated, and the widget (filter)
ends after the enclosed content (action) is evaluated.

HTTP Caching
------------
In addition to simply caching the output of an action, Yii introduced 
[CHttpCacheFilter] in version 1.1.11. This filter aids in setting the 
aforementioned headers to notify a client that a page's content has not been 
changed since the last request, so the server will not have to re-transmit the
content. [CHttpCacheFilter] can be set up similar to [COutputCache]:
~~~
[php]
public function filters()
{
    return array(
        array(
            'CHttpCacheFilter + index',
            'lastModified'=>Yii::app()->db->createCommand("SELECT MAX(`update_time`) FROM {{post}}")->queryScalar(),
        ),
    );
}
~~~
The above code will set the `Last-Modified` header to the last date at which a 
post was updated. You can also use [CHttpCacheFilter::lastModifiedExpression] to set 
the `Last-Modified` header using a php expression.

> Tip: Both, [CHttpCacheFilter::lastModifiedExpression] and 
[CHttpCacheFilter::lastModified] can take either an integer representing an 
epochal Unix timestamp or an arbitrary string representing a human-readable
date. As long as later one can be parsed by
[strtotime()](http://php.net/manual/function.strtotime.php), no further
conversion is necessary.

The "Entity Tag" (or `ETag` for short) header can be set in a similar fashion 
through [CHttpCacheFilter::etagSeed] and [CHttpCacheFilter::etagSeedExpression] 
, respectively. Both will be serialized (so you can use either a single value
or an entire array) and are used to generate a quoted, base64-encoded SHA1
hash serving as content for the `ETag` header. This differs from the way the
[Apache Webserver](http://httpd.apache.org) and others are generating their
ETags. However, this method is perfectly in line with the RFC and turned out to
be more feasible for use in a framework. 

> Note: In order to comply with
[RFC 2616, section 13.3.4](http://tools.ietf.org/html/rfc2616#section-13.3.4),
[CHttpCacheFilter] will send out `ETag` *and* `Last-Modified` headers if they
can both be generated. Consequently, both will be used for cache validation if
sent by the client.

Since entity tags are hashes, they allow more complex and/or more precise
caching strategies than `Last-Modified` headers. For instance, an ETag can be
invalidated if the site has switched to another theme.

> Tip: Expensive expressions for [CHttpCacheFilter::etagSeedExpression] may
defeat the purpose of [CHttpCacheFilter] and introduce unnecessary overhead,
since they need to be re-evaluated on every request. Try to find a simple
expression that invalidates the cache if the page *content* has been modified.

### SEO Implications
Search engine bots tend to respect cache headers. Since some crawlers have a
limit on how many pages per domain they process within a certain time span,
introducing caching headers may help indexing your site as they reduce the
number of pages that need to be processed.

